package org.testis.orm.refinedclasses;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import org.orm.PersistentException;
import org.testis.orm.classes.T_test_dataobject;
import org.testis.orm.classes.T_test_dataobjectpairs;
import org.testis.orm.classes.T_test_label;
import org.testis.orm.classes.T_test_labelpairs;
import org.testis.orm.refinedclasses.TestDesignTreeRefined.TestDesignTreeParameters;
import org.testis.utils.middleware.LabelDataHandler;
import org.testis.utils.middleware.LabelUtils;
import org.testis.utils.middleware.TestDesign;
import org.testis.utils.parametermodel.ParameterTable;

import java.util.Iterator;

/**
 * 
 * @author Cahit Emir Erel
 */
public class DataObjectRefined extends DesignPart implements ILabelContainer {

    private T_test_dataobject m_reference;
    private LinkedList<T_test_dataobjectpairs> m_keys;

    /**
     * Constructs a new DataObjectRefined with ORM object reference
     * 
     * @param reference
     *            - Data Object ORM reference
     * @throws NullPointerException
     */
    public DataObjectRefined(T_test_dataobject reference) {
	super();
	initialize();
	setReference(reference);
	T_test_dataobjectpairs[] temp = getReference().t_test_dataobjectpairs
		.toArray("rank", true);
	for (int i = 0; i < temp.length; i++) {
	    getKeys().add(temp[i]);
	}
    }

    /**
     * Initializes necessary variables
     */
    private void initialize() {
	m_keys = new LinkedList<T_test_dataobjectpairs>();
    }

    /**
     * Returns a database copy of this object
     * 
     * @return a database copy of this object
     * @throws org.orm.PersistentException
     */
    synchronized public DataObjectRefined createRefinedDatabaseCopy()
	    throws PersistentException {
	return new DataObjectRefined(createDatabaseCopy());
    }

    /**
     * Returns a database copy of the Data Object reference
     * 
     * @return a database copy of the Data Object reference
     * @throws org.orm.PersistentException
     */
    private T_test_dataobject createDatabaseCopy() throws PersistentException {
	T_test_dataobject newData = new T_test_dataobject();
	newData.setName(getReference().getName());
	newData.setModulTypeId(getReference().getModulTypeId());
	if (newData.save()) {
	    createParametersDatabaseCopy(newData);
	    return newData;
	} else {
	    throw new PersistentException(
		    "Unable to create database copy of Data Object: " + this);
	}
    }

    /**
     * Creates database copies of all keys contained by this object and connects
     * them to the Data Object reference provided
     * 
     * @param object
     *            - Data Object reference
     * @throws org.orm.PersistentException
     */
    private void createParametersDatabaseCopy(T_test_dataobject object)
	    throws PersistentException {
	T_test_dataobjectpairs[] parameters = getReference().t_test_dataobjectpairs
		.toArray();
	if (parameters != null) {
	    T_test_dataobjectpairs newPair;
	    for (int i = 0; i < parameters.length; i++) {
		newPair = new T_test_dataobjectpairs();
		newPair.setKey(parameters[i].getKey());
		if (object == null) {
		    throw new PersistentException(
			    "You can't provide null for Data Object reference");
		}
		newPair.setParent(object);
		newPair.setRank(parameters[i].getRank());
		newPair.setValue(parameters[i].getValue());
		if (!newPair.save()) {
		    throw new PersistentException(
			    "Unable to save the objectpair");
		}
	    }
	}
    }

    /**
     * Returns a LinkedHashMap containing all Keys and their Values
     * 
     * @return LinkedHashMap containing all Keys and their Values
     */
    public LinkedHashMap<String, String> getKeyValueHashMap() {
	LinkedHashMap<String, String> results = new LinkedHashMap<String, String>(
		getKeys().size());
	for (int i = 0; i < getKeys().size(); i++) {
	    results.put(getKeys().get(i).getKey(), getKeys().get(i).getValue());
	}
	return results;
    }

    /**
     * Deletes all objectpair entries that belong to the referenced object and
     * then object reference
     * 
     * @return true if delete operation is successful
     */
    @Override
    public boolean delete() {
	try {
	    deleteAllParameters();
	    return getReference().deleteAndDissociate();
	} catch (PersistentException ex) {
	    return false;
	}
    }

    /**
     * Deletes all objectpair entries in the database that belong to this Data
     * Object
     * 
     * @throws org.orm.PersistentException
     */
    public void deleteAllParameters() throws PersistentException {
	for (int i = 0; i < getKeys().size(); i++) {
	    if (!getKeys().get(i).deleteAndDissociate()) {
		throw new PersistentException();
	    }
	}
	getKeys().clear();
    }

    /**
     * Adds a new key to the database for this Data Object
     * 
     * @param key
     *            - Name of the Key
     * @param value
     *            - Value of the Key
     * @param rank
     *            - Rank of the Key
     * @throws org.orm.PersistentException
     */
    public void addParameter(String key, String value, Integer rank)
	    throws PersistentException {
	T_test_dataobjectpairs newPair = new T_test_dataobjectpairs();
	if (key != null) {
	    newPair.setKey(key);
	}
	if (value != null) {
	    newPair.setValue(value);
	} else {
	    newPair.setValue("");
	}
	if (rank != null) {
	    newPair.setRank(rank.intValue());
	}
	newPair.setParent(getReference());
	if (newPair.save()) {
	    getKeys().add(newPair);
	} else {
	    throw new PersistentException();
	}
    }

    public String toString(TestDesignTreeNode treeNode) {
	StringBuffer buffer = new StringBuffer();
	Integer expectedResult = treeNode.getReference().getParameterIntValue(
		TestDesignTreeParameters.ExpectedResultTypeId.name());
	if (expectedResult != null) {
	    buffer.append("Expected Result Type: "
		    + TestDesign.getExpectedResultTypeName(expectedResult)
		    + "\n");
	}
	buffer.append(this);
	return buffer.toString();
    }

    @Override
    public String toString() {
	try {
	    StringBuffer buffer = new StringBuffer();
	    buffer.append("Type: Data Object");
	    buffer.append("\nName: " + getName());
	    buffer.append("\nModulType: "
		    + TestDesign.getModuleTypeName(getReference()
			    .getModulTypeId()));

	    if (getKeys() != null && getKeys().size() > 0) {
		buffer.append("\nParameters: ");
		for (int i = 0; i < getKeys().size(); i++) {
		    buffer.append("\n\t" + getKeys().get(i).getKey() + "="
			    + getKeys().get(i).getValue());
		}
	    } else {
		buffer.append("\nNo parameters found");
	    }
	    // buffer.append("\n\n\nLabels:" + getLabelsAsString(false));
	    buffer.append("\n\n\nLabels: ");
	    T_test_label[] labels = getLabelsAsArray();
	    if (labels != null && labels.length > 0) {
		for (int i = 0; i < labels.length; i++) {
		    buffer.append("\n\t" + labels[i].getName());
		}
	    } else {
		buffer.append("\tNo labels found");
	    }
	    return buffer.toString();
	} catch (NullPointerException ex) {
	    ex.printStackTrace();
	    return null;
	}
    }

    /**
     * Returns the Key Linked List
     * 
     * @return the Key Linked List
     */
    private LinkedList<T_test_dataobjectpairs> getKeys() {
	return m_keys;
    }

    /**
     * Returns the Data Object ORM reference
     * 
     * @return the Data Object ORM reference
     */
    public T_test_dataobject getReference() {
	return m_reference;
    }

    /**
     * Sets the Data Object ORM reference
     * 
     * @param reference
     *            - Data Object ORM reference
     * @throws NullPointerException
     */
    private void setReference(T_test_dataobject reference) {
	if (reference == null) {
	    throw new NullPointerException();
	}
	m_reference = reference;
    }

    /**
     * Returns Type name of this Test Design part type
     * 
     * @return Type name of this Test Design part type
     */
    public String getTypeName() {
	return "DataObject";
    }

    private int compare(Comparable source) {
	DataObjectRefined sourceDataObject = (DataObjectRefined) source;
	if (sourceDataObject.getReference().getModulTypeId() != getReference()
		.getModulTypeId()) {
	    return Comparable.DIFFERENT_PROPERTIES;
	}
	LinkedList<T_test_dataobjectpairs> keys = sourceDataObject.getKeys();
	if (keys.size() != getKeys().size()) {
	    return Comparable.DIFFERENT_PARAMETERS;
	} else {
	    for (int i = 0; i < keys.size(); i++) {
		if (!(keys.get(i).getKey().equals(getKeys().get(i).getKey()) && keys
			.get(i).getValue().equals(getKeys().get(i).getValue()))) {
		    return Comparable.DIFFERENT_PARAMETERS;
		}
	    }
	}
	return Comparable.EXACT;
    }

    @Override
    public int compare(Comparable source, Comparable treeReference) {
	TestDesignTreeNode src = (TestDesignTreeNode) source;
	TestDesignTreeNode thisTree = (TestDesignTreeNode) treeReference;
	Integer sourceRes = src.getReference().getParameterIntValue(
		TestDesignTreeParameters.ExpectedResultTypeId.name());
	Integer thisTreeRes = thisTree.getReference().getParameterIntValue(
		TestDesignTreeParameters.ExpectedResultTypeId.name());
	if (sourceRes != thisTreeRes) {
	    return Comparable.DIFFERENT_PROPERTIES;
	}
	return compare(src.getObject());
    }

    private boolean hasSameStaticParameters(HashMap<String, Object> params,
	    Comparable treeTreference) {
	ParameterTable parameterTableModel = (ParameterTable) params
		.get("parameterTableModel");
	String expectedResultType = (String) params.get("expectedResultType");
	LinkedList<String> labels = (LinkedList<String>) params.get("labels");
	TestDesignTreeRefined designtree = ((TestDesignTreeNode) treeTreference)
		.getReference();
	DataObjectRefined doRefined = (DataObjectRefined) ((TestDesignTreeNode) treeTreference)
		.getObject();
	if (doRefined != null) {
	    HashMap myLabels = doRefined.getLabelsAsHashMap();
	    if (myLabels.size() != labels.size()) {
		return false;
	    }

	    Iterator iterator = myLabels.keySet().iterator();
	    while (iterator.hasNext()) {
		T_test_label myLabel = (T_test_label) myLabels.get(iterator
			.next());
		if (!labels.contains(myLabel.getName())) {
		    return false;
		}
	    }
	}
	Integer expectedResultTypeId = designtree
		.getParameterIntValue(TestDesignTreeParameters.ExpectedResultTypeId
			.name());

	if (expectedResultTypeId != null
		&& TestDesign.getExpectedResultTypeName(expectedResultTypeId)
			.equals(expectedResultType)) {
	    boolean foundParameter = true;
	    if (getKeys().size() != parameterTableModel.getRowCount()) {
		return false;
	    }
	    for (int i = 0; i < parameterTableModel.getRowCount(); i++) {
		if (!parameterTableModel.getItem(i).getParameter()
			.equals(getKeys().get(i).getKey())
			|| !parameterTableModel.getItem(i).getValue()
				.equals(getKeys().get(i).getValue())) {
		    foundParameter = false;
		    break;
		}
	    }
	    return foundParameter;
	} else {
	    return false;
	}
    }

    @Override
    public boolean compareParameters(HashMap<String, Object> params,
	    Comparable treeTreference) {
	return hasSameStaticParameters(params, treeTreference);
    }

    public boolean hasSameRunTimeParams(HashMap<String, Object> params,
	    Comparable treeTreference) {
	return true;
    }

    public T_test_label[] getLabelsAsArray() {
	T_test_label[] labels = null;
	LabelDataHandler handler = new LabelDataHandler();
	if (getReference().t_test_labelpairs != null
		&& getReference().t_test_labelpairs.size() > 0) {
	    T_test_labelpairs[] labelpairs = getReference().t_test_labelpairs
		    .toArray();
	    labels = new T_test_label[labelpairs.length];
	    for (int i = 0; i < labelpairs.length; i++) {
		labels[i] = handler.getLabelById(labelpairs[i].getLabelId());
	    }
	} else {
	    labels = new T_test_label[0];
	}
	return labels;
    }

    public HashMap getLabelsAsHashMap() {
	HashMap result = new HashMap();
	LabelDataHandler handler = new LabelDataHandler();
	if (getReference().t_test_labelpairs != null
		&& getReference().t_test_labelpairs.size() > 0) {
	    T_test_labelpairs[] labelpairs = getReference().t_test_labelpairs
		    .toArray();
	    for (int i = 0; i < labelpairs.length; i++) {
		T_test_label label = handler.getLabelById(labelpairs[i]
			.getLabelId());
		result.put(label.getId(), label);
	    }
	}
	return result;
    }

    public String getLabelsAsString(boolean inheritance) {
	return LabelUtils
		.convertLabelsToString(getLabelsAsArray(), inheritance);
    }

    public boolean matchesLabelPattern(String regex) {
	return LabelUtils.matchesLabelPattern(regex, getLabelsAsString(true));
    }
}
